package jmine.tec.persist.schema.impl.elements;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import jmine.tec.persist.PersistMessages;
import jmine.tec.persist.schema.api.AbstractProcessingSchemaElement;
import jmine.tec.persist.schema.api.ProcessingSchemaElement;
import jmine.tec.persist.schema.api.SqlDialect;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;

/**
 * Elemento que representa uma alternate key (unique constraint).
 * 
 * @author lundberg
 */
public class AlternateKeyElement extends AbstractProcessingSchemaElement<TableElement> implements Comparable<AlternateKeyElement> {
    private final List<String> columns;

    private String table;

    private String name;

    /**
     * Construtor
     * 
     * @param parent parent
     */
    public AlternateKeyElement(ProcessingSchemaElement<?> parent) {
        super(parent);
        this.columns = new LinkedList<String>();
    }

    /**
     * {@inheritDoc}
     */
    public List<String> createStatements(SqlDialect dialect) {
        return dialect.renderAlternativeKey(this.table, this.name, this.columns);
    }

    /**
     * Processa a chave, obtendo um nome gerado automaticamente caso o nome atual não esteja definido.
     * 
     * @param tableElement tabela que possui a chave
     */
    public void process(TableElement tableElement) {
        if (StringUtils.isBlank(this.getName())) {
            this.name = tableElement.generateAkName();
            this.warn(PersistMessages.ALTERNATE_KEY_NAME_NOT_DEFINED.create(this.name, StringUtils.join(this.columns, ", "),
                    tableElement.getName()).getMessage());
        } else {
            if (!this.name.startsWith(String.format("AK_%s_", tableElement.getAlias()))) {
                this.warn(PersistMessages.ALTERNATE_KEY_NONSTANDART_NAME.create(this.name, this.table, tableElement.getAlias())
                        .getMessage());
            }

            for (String columnName : this.getColumns()) {
                ColumnElement column = tableElement.getColumns().get(columnName);
                if (column == null) {
                    this.warn(PersistMessages.ALTERNATE_KEY_COLUMN_NOT_FOUND.create(columnName, this.name, this.table).getMessage());
                } else if (column.isNullable()) {
                    this.warn(PersistMessages.ALTERNATE_KEY_NULLABLE_COLUMN.create(column.getName(), this.name, this.table).getMessage());
                }
            }
        }
    }

    /**
     * Adiciona uma coluna à chave
     * 
     * @param column nome da coluna a ser adicionada.
     */
    public void addColumn(String column) {
        this.columns.add(column);
    }

    /**
     * Define o nome da constraint
     * 
     * @param name novo nome da constraint
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return nome da chave
     */
    public String getName() {
        return this.name;
    }

    /**
     * Define o nome da tabela que possui a chave
     * 
     * @param table nome da tabela
     */
    public void setTable(String table) {
        this.table = table;
    }

    /**
     * {@inheritDoc}
     */
    public int compareTo(AlternateKeyElement o) {
        CompareToBuilder builder = new CompareToBuilder();
        builder.append(this.table, o.table);
        builder.append(this.name, o.name);
        builder.append(this.columns.size(), o.columns.size());
        if (builder.toComparison() == 0) {
            Iterator<String> it = this.columns.iterator();
            Iterator<String> oit = o.columns.iterator();
            while (it.hasNext()) {
                builder.append(it.next(), oit.next());
            }
        }
        return builder.toComparison();
    }

    /**
     * @return nome da tabela que possui a chave
     */
    public String getTable() {
        return this.table;
    }

    /**
     * @return the columns
     */
    public List<String> getColumns() {
        return this.columns;
    }

    /**
     * @param moreColumns moreColumns
     */
    public void addColumns(List<String> moreColumns) {
        this.columns.addAll(moreColumns);
    }
}
